﻿//-----------------------------------------------------------------------
// <copyright file="IoC.cs" company="WpfDevelopment.com">
//     Copyright WpfDevelopment.com. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace WpfDevelopment.MVVM
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Globalization;
    using System.Reflection;
    using System.Resources;
    using System.Diagnostics.Contracts;

    /// <summary>
    /// Container around actual implementation of IoC, so it's easier to swap.
    /// </summary>
    public abstract class IoC
    {
        /// <summary>
        /// The active ioc container
        /// </summary>
        private static IoC container = null;

        /// <summary>
        /// The object to create a lock for the setting of the ioc instance
        /// </summary>
        private static object syncRoot = new object();

        /// <summary>
        /// Gets the container.
        /// </summary>
        /// <value>The container.</value>
        public static IoC Container
        {
            get
            {
                if (container == null)
                {
                    lock (syncRoot)
                    {
                        if (container == null)
                        {
                            LoadContainer();
                        }
                    }
                }

                return container;
            }
        }

        /// <summary>
        /// Sets the container.
        /// </summary>
        /// <param name="instance">The instance to set inside the container.</param>
        public static void SetContainer(IoC instance)
        {
            lock (syncRoot)
            {
                container = instance;
            }
        }

        #region [ abstract ]

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        public abstract void Initialize();

        /// <summary>
        /// Gets this instance.
        /// </summary>
        /// <typeparam name="T">Type of instance to create</typeparam>
        /// <returns>The instance requested of type T</returns>
        public abstract T GetInstanceOf<T>();

        /// <summary>
        /// Gets the specified name.
        /// </summary>
        /// <typeparam name="T">Type of instance to create</typeparam>
        /// <param name="name">The name of the instance to get.</param>
        /// <returns>The instance requested of type T</returns>
        public abstract T GetInstanceByName<T>(string name);

        /// <summary>
        /// Gets all instances by the given type.
        /// </summary>
        /// <typeparam name="T">The type of instances to get</typeparam>
        /// <returns>all the instances by the given type that are registered to the ioc container</returns>
        public abstract IEnumerable<T> GetAll<T>();

        /// <summary>
        /// Creates the singleton.
        /// </summary>
        /// <typeparam name="T">Type of instance to create</typeparam>
        /// <param name="name">The name of the instance to create.</param>
        /// <returns>An new instance of the requested type of T</returns>
        public abstract T CreateSingleton<T>(string name);

        #endregion

        /// <summary>
        /// Loads the container.
        /// </summary>
        private static void LoadContainer()
        {
            // The resource manager to use
            ResourceManager stringManager = new ResourceManager("WpfDevelopment.MVVM.DI.IoC", Assembly.GetAssembly(typeof(IoC)));

            // Load the ioc section from the web.config
            IoCConfigurationSection config = ConfigurationManager.GetSection("ioc") as IoCConfigurationSection;
            Throw<ConfigurationException>.When(
                config == null,
                stringManager.GetString("configurationSectionForIoCCouldNotBeFound", CultureInfo.CurrentUICulture));

            Contract.Assume(config != null);

            // Get the type to instantiate
            Type type = Type.GetType(config.TypeOfContainer);

            Throw<InvalidOperationException>.When(
                type.BaseType != typeof(IoC),
                stringManager.GetString("typeOfIoCcontainerDoesNotInheritFromBaseIoCClass", CultureInfo.CurrentUICulture));

            // Create the instance of the ioc container
            IoC container = Activator.CreateInstance(type) as IoC;
            Throw<InvalidProgramException>.When(
                container == null,
                stringManager.GetString("unableToActivateIoCInstance"));

            // Init & set the container singleton
            container.Initialize();
            SetContainer(container);
        }
    }
}
